local _ENV,_START = hf_setenv('table_mgr',true)

local util = require "util"
local socket = require "skynet.socket"
local skynet = require "skynet.manager"
local server_def = require "server_def"
local error_code = require "error_code"
local table_def = require "table_def"
local game_def = require "game_def"
local server_config = dofile(skynet.getenv("server_config"))
local sharedata  = require "skynet.sharedata"
local cocall = require "cocall"
local math_min = math.min
local math_random = math.random
local table_sort = table.sort
local table_insert = table.insert
local table_remove = table.remove

--math.randomseed(os.time())

local phase_swith_time = server_config.phase_swith_time
local TABLE_LIMIT = server_config.table_limit or 1000
local MAX_MATCH_TIME = server_config.max_match_time or 3
local CHANGE_TABLE_NUM = 3
local DEBUG = server_config.debug

function init()
    global_configs = nil
    server_start_time = 0
    asyn_data_time = 0
    last_sync_time = 0
    --缓存正在匹配中的玩家的数据
    match_data_list       = {}
    matched_list          = {}
    hunderd_list          = {}
    tablesvr_table_num    = {} --标记每个桌子节点中所有桌子数量
    tablesvr_table_player_num = {}
    tablesvr_game_type_map = {}
    closing_tablesvr_map   = {}
    tablesvr_ack_time      = {}
    player_match_type_map  = {}  --标记玩家是否正在匹配中
    table_type_ip_limit    = {}  --桌子是否有IP限制
    start_load = false           -- 开始初始化百人场游戏
    hf_set_sentinel_end(_ENV)
end

local CMD 					= {}
local try_total_match_times = 10 --每个玩家尝试匹配的次数 10次失败后那么就不帮其匹配
local clear_match_status_time_out = 60 * 2

local table_patterns = {
    LEGACY = 0,
    GLOBAL_CONTROL = 1,
    PLAYER_CONTROL = 2,
    PLAYER_PRIOR = 3,
    CHANNEL_LIMIT = 4,
}

local function send_data_to_player(uid,...)
	R().hallsvr{key=uid}:send('.msg_handler','toagent',uid,...)
end

local function match_sort(a,b)
	return a.coins < b.coins
end

local function is_table_svr_close(tablesvr_id)
    if closing_tablesvr_map[tablesvr_id] then
        return true
    end
    return false
end

local function select_one_table_server(table_type)
	-- if table_def.IS_TABLE_RANDOM then
	-- 	local candidate_svr_id,least_table_num = next(tablesvr_table_num)
	-- 	--策略是选一个最少桌子数的tablesvr
	-- 	for tablesvr_id,table_num in pairs(tablesvr_table_num) do
	-- 		if table_num < TABLE_LIMIT and not closing_tablesvr_map[tablesvr_id] then
	-- 			if table_num < least_table_num then
	-- 				least_table_num = table_num
	-- 				candidate_svr_id = tablesvr_id
	-- 			end
	-- 		end
	-- 	end
	-- 	return candidate_svr_id
	-- else
	-- 	local game_type = game_def.get_game_type(table_type)
	-- 	if table_def.table_svr_router[game_type] and closing_tablesvr_map[tablesvr_id] then
	-- 		return table_def.table_svr_router[game_type]
	-- 	else
	-- 		errlog("not find table_type register server",game_type)
	-- 	end
	-- end

    dbglog("select_one_table_server table_type:",table_type)
    local candidate_svr_id, least_table_num
    for tablesvr_id, table_num in pairs(tablesvr_table_num) do
        local game_type = assert(game_def.get_game_type(table_type))
        if tablesvr_game_type_map[tablesvr_id] == 0 or tablesvr_game_type_map[tablesvr_id] == game_type and not is_table_svr_close(tablesvr_id) then
            if table_num < TABLE_LIMIT and not closing_tablesvr_map[tablesvr_id] then
                if not least_table_num or table_num < least_table_num then
                    least_table_num = table_num
                    candidate_svr_id = tablesvr_id
                end
            end
        end
    end
    dbglog("candidate_svr_id table_type:",candidate_svr_id)
    return candidate_svr_id
end

--获取指定游戏类型房间数目
local function get_table_type_num(tab_type)
    local num = 0
    for tablesvr_id,table_type_stats_list in pairs(tablesvr_table_player_num) do
        if not is_table_svr_close(tablesvr_id) then
            for table_type,table_stats_list in pairs(table_type_stats_list) do
                if table_type == tab_type then
                    num = num + #table_stats_list
                end
            end
        end
    end
    return num 
end

local function match_player_on_a_table(table_stats,match_type_list,end_index)
	if end_index <= 0 then
		return false
	end
	--如果桌子节点已经关闭 那么撮合一张桌子失败
	-- if is_table_svr_close(server_id) then
	-- 	errlog(server_id, "match_player_on_a_table but tablesvr is close")
	-- 	return false
	-- end

	--如果桌子节点还存活着 那么进行撮合
	local tmp_table = {}
	for i = 1, end_index do
		table_insert(tmp_table, match_type_list[i])
	end

	table_insert(matched_list,{tablesvr_id = table_stats.table_gid >> 32, matched_gid = 0,
                               table_gid = table_stats.table_gid,table_type = table_stats.table_type,
                               users_data = tmp_table})

	for i = 1, end_index do
		table_remove(match_type_list, 1)
	end

	--将剩余人数做一下减法运算
	table_stats.left_num = table_stats.left_num - end_index
end

local function new_match_player_on_a_table(tablesvr_id,end_index,max_player_num,match_type_list,
                                           table_type,table_pattern,table_conf)
    local matched_player_count = 0
    for i = 1, end_index, max_player_num do
        -- local tablesvr_id = select_one_table_server(table_type)
        -- if not tablesvr_id or is_table_svr_close(tablesvr_id) then
        --     errlog("cant find available table server")
        --     break
        -- end
        local user_data_list = {}
        for j = 1, max_player_num do
            matched_player_count = matched_player_count + 1
            if matched_player_count > end_index then
                break
            end
            table_insert(user_data_list, table_remove(match_type_list, 1))
        end
        local matched_gid = tablesvr_id << 32 | table_type
        table_insert(matched_list, {
            tablesvr_id = tablesvr_id, 
            matched_gid = matched_gid, 
            table_gid   = 0,
            table_type  = table_type, 
            users_data  = user_data_list,
            table_pattern = table_pattern, 
            table_conf  = table_conf})
    end
end

local function get_diff_ip_match_length(match_type_list)
    if #match_type_list <= 0 then
        return 0
    end

    local ip_list_map = {}
    local length = #match_type_list
    for i=#match_type_list,1,-1 do
        local login_ip = match_type_list[i].login_ip or 0
        if ip_list_map[login_ip] then
            table_insert(match_type_list,table_remove(match_type_list,i))
            length = length - 1
        end
        ip_list_map[login_ip] = true
    end
    return length
end

local function classify_limit_user_data(match_type_list)
    local players_limited = {}
    local players_not_limited = {}
    for _, user_data in pairs(match_type_list) do
        if user_data.channel_limit then
            table_insert(players_limited, user_data)
        else
            table_insert(players_not_limited, user_data)
        end
    end
    return players_limited, players_not_limited
end

local function table_concat(dst, src)
    for _, v in pairs(src) do
        table_insert(dst, v)
    end
end

local function clear_table(t)
    while #t ~= 0 do
        table_remove(t)
    end
end

local function common_match_player(table_type,match_type_list)
	local game_type = game_def.get_game_type(table_type)
	local max_player_num = table_def.game_player_num[game_type]

    local tablesvr_id = select_one_table_server(table_type)
    if not tablesvr_id then
        return
    end

    local players_limited, players_not_limited = classify_limit_user_data(match_type_list)

    local ip_limit = table_type_ip_limit[table_type]
	--大于三人的时候,三人凑一桌
    if #players_not_limited >= max_player_num then
         --如果需要检查IP,有IP相同的,将IP相同的玩家放入匹配队列的末尾
        local length = ip_limit and get_diff_ip_match_length(players_not_limited) or #players_not_limited
        local end_index = math_floor(length / max_player_num) * max_player_num
        
		new_match_player_on_a_table(tablesvr_id,end_index,max_player_num,players_not_limited,table_type,table_patterns.LEGACY)
	end

    --少于三人的情况,有人超过等待时间,就凑一桌
    local length = ip_limit and get_diff_ip_match_length(players_not_limited) or #players_not_limited
	if length > 0 and length < max_player_num then
        local match_type = assert(table_def.match_config[game_type])
		if match_type == table_def.match_types.LEGACY_COMMON then
			local time_out = false
			local now = util.get_now_time()
			for _,v in pairs(players_not_limited) do
				if now - v.begin_time >= MAX_MATCH_TIME then
					time_out = true
                    break
				end
			end
			if time_out then
				new_match_player_on_a_table(tablesvr_id,length,max_player_num,players_not_limited,
                                            table_type,table_patterns.LEGACY)
			end
		else
			new_match_player_on_a_table(tablesvr_id,length,max_player_num,players_not_limited,
                                        table_type,table_patterns.LEGACY)
		end
    end
    
    --匹配新桌给渠道限制的玩家
    if #players_limited > 0 then
        new_match_player_on_a_table(tablesvr_id,#players_limited, 1, players_limited,
                                            table_type, table_patterns.CHANNEL_LIMIT)
    end

    clear_table(match_type_list)
    table_concat(match_type_list, players_not_limited)
    table_concat(match_type_list, players_limited)
end

local function special_match_player(table_type,match_type_list)
	--如果当前匹配人数为空直接返回
	if #match_type_list == 0 then
		return
	end

    local tablesvr_id = select_one_table_server(table_type)
    if not tablesvr_id then
        return
    end

	--刷选出正在等待换桌的人
	local trans_table_list = {}
	local index_list = {}
	for index, match_info in pairs(match_type_list) do
		if match_info.old_table_gid then
			table_insert(trans_table_list,match_info)
			table_insert(index_list,index)
		end
	end

	for i=#index_list,1,-1 do
		table_remove(match_type_list,index_list[i])
	end

	--对老桌子进行合理分配
	local table_type_stats_list = tablesvr_table_player_num[tablesvr_id]
    if table_type_stats_list then
        local table_stats_list = table_type_stats_list[table_type]
        if table_stats_list then
            for _, table_stats in pairs(table_stats_list) do
                local table_pattern = table_stats.table_pattern or table_patterns.LEGACY
                if table_pattern == table_patterns.LEGACY then
                    --如果当前该桌子的人数还可以承载所有的匹配人数 那么将所有玩家拉到该桌子
                    if table_stats.left_num >= #match_type_list then
                        match_player_on_a_table(table_stats,match_type_list,#match_type_list)
                    elseif table_stats.left_num > 0 then
                        match_player_on_a_table(table_stats,match_type_list,table_stats.left_num)
                    end

                    if #match_type_list == 0 then
                        break
                    end
                end
            end
        end
    end

	--对换桌的人进行分配
	while #trans_table_list > 0 do
		local have_matched = false
		local trans_data = trans_table_list[1]
		local table_type_stats_list = tablesvr_table_player_num[tablesvr_id]
        if table_type_stats_list then
            local table_stats_list = table_type_stats_list[table_type]
            for _, table_stats in pairs(table_stats_list) do
                local table_pattern = table_stats.table_pattern or table_patterns.LEGACY
                if table_pattern == table_patterns.LEGACY then
                    if table_stats.table_gid ~= trans_data.old_table_gid and table_stats.left_num > 0 then
                        match_player_on_a_table(table_stats,trans_table_list,1)
                        have_matched = true
                    end
                end
            end
        end
        if not have_matched then
            table_insert(match_type_list,trans_data)
            table_remove(trans_table_list, 1)
        end
	end

    common_match_player(table_type,match_type_list)
end

local function filter_user_data(match_type_list, cond_func, count)
    local user_data_list = {}

	local index_list = {}
    local n = 0
	for index, user_data in pairs(match_type_list) do
		if cond_func(user_data) then
			table_insert(user_data_list, user_data)
			table_insert(index_list, index)
            n = n + 1
            if count and n == count then
                break
            end
		end
	end

	for i = #index_list, 1, -1 do
		table_remove(match_type_list, index_list[i])
	end

    return user_data_list
end

local function get_player_ctrl_value(user_data)
    if game_def.get_game_type(user_data.table_type) == 6 then
        return 0
    end
    
    local ok, ret = R().basesvr({key=user_data.uid}):call('.msg_handler', 'is_affected', user_data.uid)
    if not ok or not ret then
        return 0
    else
        return 1
    end

    return 0
end

local function classify_user_data(match_type_list)
    local players_controlled = {}
    local players_not_controlled = {}
    for _, user_data in pairs(match_type_list) do
        if get_player_ctrl_value(user_data) > 0 then
            table_insert(players_controlled, user_data)
        else
            table_insert(players_not_controlled, user_data)
        end
    end
    return players_controlled, players_not_controlled
end

local function hit(probability)
    local value = math_random(0, 99)
    return value < probability
end

local function is_in(itable, value)
    if not itable then
        errlog("table is nil")
        return false
    end

    for _, element in pairs(itable) do
        if element == value then
            return true
        end
    end
    return false
end

local function get_table_by_cond(table_type, cond_func)
    for tablesvr_id, table_type_stats_list in pairs(tablesvr_table_player_num) do
        local table_stats_list = table_type_stats_list[table_type]
        if table_stats_list then
            for _, table_stats in pairs(table_stats_list) do
                if cond_func(table_stats) then
                    return table_stats
                end
            end
        end
    end
    return nil
end

local function get_max_human_can_enter_table(table_type,user_data)
    local max_human_stats 
    local ip_limit = table_type_ip_limit[table_type]
    for tablesvr_id,table_type_stats_list in pairs(tablesvr_table_player_num) do
        local table_stats_list = table_type_stats_list[table_type]
        print("get_max_human_can_enter_table======",tostring_r(table_stats_list))
        if table_stats_list then
            for _, table_stats in pairs(table_stats_list) do 
                local table_pattern = table_stats.table_pattern or table_patterns.LEGACY
                if table_pattern == table_patterns.GLOBAL_CONTROL and table_stats.left_num > 0 then
                    if ip_limit and is_in(table_stats.player_ip_list,user_data.login_ip) then --ip限制
                        goto continue
                    end
                    if not max_human_stats or (max_human_stats and (table_stats.human_num or 0) > (max_human_stats.human_num or 0) ) then
                        max_human_stats = table_stats
                    end
                end
                ::continue::
            end
        end
    end

    return max_human_stats
end

local function get_can_change_table(table_type,user_data)
    local select_table_one = {}
    local select_table_two = {}
    local ip_limit = table_type_ip_limit[table_type]
    for tablesvr_id, table_type_stats_list in pairs(tablesvr_table_player_num) do
        local table_stats_list = table_type_stats_list[table_type]
        if not table_stats_list then
            goto continue
        end
        for _, table_stats in pairs(table_stats_list) do 
            if ip_limit and is_in(table_stats.player_ip_list,user_data.login_ip) then
                goto continue
            end

            local pattern = table_stats.table_pattern or table_patterns.LEGACY
            if pattern == table_patterns.GLOBAL_CONTROL and table_stats.left_num > 0 and table_stats.table_gid ~= user_data.old_table_gid then
                if (table_stats.human_num or 0) >= CHANGE_TABLE_NUM then
                    table_insert(select_table_one,table_stats)
                else
                    table_insert(select_table_two,table_stats)
                end
            end
            ::continue::
        end
        ::continue::
    end

    if next(select_table_one) then
        return select_table_one[math_random(1,#select_table_one)]
    end
    if next(select_table_two) then
        return select_table_two[math_random(1,#select_table_two)]
    end
end

local function new_common_match_player(table_type, match_type_list)

    --因为table_type在启动的时候就已经确定好桌子进程节点了,所以此处只需要找出该table_type
    --对应在哪个桌子进程节点,然后将玩家送进该玩家即可
    -- local tablesvr_id = select_one_table_server(table_type)
    -- if not tablesvr_id then
    --     return
    -- end

    --classify_user_data该功能之前已经废弃
    --local players_controlled, players_not_controlled = classify_user_data(match_type_list)

	--对老桌子进行合理分配
    for tablesvr_id, table_type_stats_list in pairs(tablesvr_table_player_num) do
        local table_stats_list = table_type_stats_list[table_type]
        -- print("____2___tablesvr_table_player_num____________", table_stats_list)
        if table_stats_list then
            --理论上百人场一个table_type只会有一个场次
            for _, table_stats in pairs(table_stats_list) do
                --匹配服不管桌子人数有没有满，统一都送给桌子服务，具体能不能进桌子，通过req_enter去进行判断房间人数是否已满
                local count = #match_type_list
                match_player_on_a_table(table_stats, match_type_list, count)
                return
            end
        end
    end

	-- local game_type = game_def.get_game_type(table_type)
	-- local max_player_num = table_def.game_player_num[game_type]

 --    -- 分配新桌子
 --    new_match_player_on_a_table(tablesvr_id,#match_type_list, max_player_num, match_type_list,
 --                                table_type, table_patterns.GLOBAL_CONTROL)

 --    -- 分配新桌子给受控玩家
 --    new_match_player_on_a_table(tablesvr_id,#players_controlled, 1, players_controlled,
 --                                table_type, table_patterns.PLAYER_CONTROL)

 --    clear_table(match_type_list)
 --    table_concat(match_type_list, players_not_controlled)
 --    table_concat(match_type_list, players_controlled)
end

local function hbsl_match_player(table_type, match_type_list)

    --因为table_type在启动的时候就已经确定好桌子进程节点了,所以此处只需要找出该table_type
    --对应在哪个桌子进程节点,然后将玩家送进该玩家即可
    local tablesvr_id = select_one_table_server(table_type)
    if not tablesvr_id then
        return
    end

    --classify_user_data该功能之前已经废弃
    --local players_controlled, players_not_controlled = classify_user_data(match_type_list)

    --对老桌子进行合理分配
    for tablesvr_id, table_type_stats_list in pairs(tablesvr_table_player_num) do
        local table_stats_list = table_type_stats_list[table_type]
        -- print("____2___tablesvr_table_player_num____________", table_stats_list)
        if table_stats_list then
            --理论上百人场一个table_type只会有一个场次
            for _, table_stats in pairs(table_stats_list) do
                --匹配服不管桌子人数有没有满，统一都送给桌子服务，具体能不能进桌子，通过req_enter去进行判断房间人数是否已满
                local count = #match_type_list
                match_player_on_a_table(table_stats, match_type_list, count)
                return
            end
        end
    end

    local game_type = game_def.get_game_type(table_type)
    local max_player_num = table_def.game_player_num[game_type]

    -- 分配新桌子
    new_match_player_on_a_table(tablesvr_id,#match_type_list, max_player_num, match_type_list,
                                table_type, table_patterns.GLOBAL_CONTROL)

 --    -- 分配新桌子给受控玩家
 --    new_match_player_on_a_table(tablesvr_id,#players_controlled, 1, players_controlled,
 --                                table_type, table_patterns.PLAYER_CONTROL)

 --    clear_table(match_type_list)
 --    table_concat(match_type_list, players_not_controlled)
 --    table_concat(match_type_list, players_controlled)
end

local function is_ip_not_in_table(ip,ip_list)
    if not ip_list then
        return true
    end

    for i=1,#ip_list do
        if ip == ip_list[i] then
            return false
        end
    end
    return true
end

local function new_special_match_player(table_type, match_type_list)
    local tablesvr_id = select_one_table_server(table_type)
    if not tablesvr_id then
        return
    end

    --刷选出正在等待换桌的人
    local trans_table_list = filter_user_data(match_type_list,function(user_data) return user_data.old_table_gid end)

    --正常匹配
    local players_controlled, players_not_controlled = classify_user_data(match_type_list)
    while #players_not_controlled ~= 0 do
        local user_data = players_not_controlled[1]
        local table_stats = get_max_human_can_enter_table(table_type,user_data)
        if table_stats then
            match_player_on_a_table(table_stats, players_not_controlled, 1)
        else
            new_match_player_on_a_table(tablesvr_id,#players_not_controlled, 1, players_not_controlled,
                                            table_type, table_patterns.GLOBAL_CONTROL, {robot_num=1})
        end
    end

    --分配新桌子给受控玩家
    local game_type = game_def.get_game_type(table_type)
    local max_player_num = table_def.game_player_num[game_type] - 1
    new_match_player_on_a_table(tablesvr_id,#players_controlled, 1, players_controlled,
                                table_type, table_patterns.PLAYER_CONTROL, {robot_num=1})

    clear_table(match_type_list)
    table_concat(match_type_list, players_not_controlled)
    table_concat(match_type_list, players_controlled)

    --换桌匹配
    players_controlled, players_not_controlled = classify_user_data(trans_table_list)
    --print("new_special_match_player=========222",tostring_r(players_not_controlled))
    while #players_not_controlled ~= 0 do
        local user_data = players_not_controlled[1]
        local table_stats = get_can_change_table(table_type,user_data)
        print("new_special_match_player==333333333",table_stats,tostring_r(table_stats))
        if table_stats then
            match_player_on_a_table(table_stats, players_not_controlled, 1)
        else
            new_match_player_on_a_table(tablesvr_id,#players_not_controlled, 1, players_not_controlled,
                                            table_type, table_patterns.GLOBAL_CONTROL, {robot_num = 1})
        end
    end

    --分配新桌子给换桌的受控玩家
    new_match_player_on_a_table(tablesvr_id,#players_controlled, 1, players_controlled,
                                table_type, table_patterns.PLAYER_CONTROL, {robot_num=1})

    table_concat(match_type_list, players_not_controlled)
    table_concat(match_type_list, players_controlled)
end

local function match_player(table_type,match_type_list)
    if not next(match_type_list) then
        return
    end
	local game_type = game_def.get_game_type(table_type)
    --print("match_player111111111111",game_type)
    local match_type = assert(table_def.match_config[game_type])

    -- dbglog("match_player:", game_type, match_type)

	if match_type == table_def.match_types.LEGACY_COMMON then
		common_match_player(table_type,match_type_list)
	elseif match_type == table_def.match_types.LEGACY_SPECIAL then
		special_match_player(table_type,match_type_list)
    elseif match_type == table_def.match_types.NEW_COMMON then
		new_common_match_player(table_type,match_type_list)
    elseif match_type == table_def.match_types.NEW_SPECIAL then
		new_special_match_player(table_type,match_type_list)
    elseif match_type == table_def.match_types.HBSL then
        hbsl_match_player(table_type,match_type_list)
    else
        assert(false, "unknown match_type")
	end
end

--放回匹配中
local function return_back_player(table_type,failed_list)
	local need_sort = false
	for _, user_data in pairs(failed_list) do
		print("===========return back player==========",tostring_r(user_data))
		if user_data.try_match_times <= try_total_match_times then
			table_insert(match_data_list[table_type],user_data)
			need_sort = true
		else
			player_match_type_map[user_data.uid] = nil
		end
	end

	--排一下序
	if need_sort then
		table_sort(match_data_list[table_type],match_sort)
	end
end

local function delete_table_stats(table_stats)
	local tablesvr_id = table_stats.table_gid >> 32
	local tablesvr_stats_list = tablesvr_table_player_num[tablesvr_id]
	if tablesvr_stats_list then
		local table_list = tablesvr_stats_list[table_stats.table_type]
		if table_list then
			for index, _table_stats in pairs(table_list) do
				if _table_stats.table_gid == table_stats.table_gid then
					table_remove(table_list,index)
					break
				end
			end
		end
	end
end

local function table_state_sort(a,b)
	return a.left_num < b.left_num
end

local function update_table_stats(table_stats)
    -- if table_stats.table_type == 300 then
    --     errlog("_______1111____update_table_stats_____", tostring_r(table_stats))
    -- end
	local tablesvr_id = table_stats.table_gid >> 32
	local tablesvr_stats_list = tablesvr_table_player_num[tablesvr_id]
	--更新房间信息
	if tablesvr_stats_list then
		local table_list = tablesvr_stats_list[table_stats.table_type]
		if not table_list then
			tablesvr_stats_list[table_stats.table_type] = {table_stats}
		else
			local is_add = true
			for _, _table_stats in pairs(table_list) do
				if _table_stats.table_gid == table_stats.table_gid then
					_table_stats.left_num = table_stats.left_num
                    _table_stats.robot_num = table_stats.robot_num
                    _table_stats.human_num = table_stats.human_num
					_table_stats.players_never_start = table_stats.players_never_start
                    _table_stats.table_pattern = table_stats.table_pattern
                    _table_stats.player_ip_list = table_stats.player_ip_list
					is_add = false
					break
				end
			end
			if is_add then
				table_insert(table_list,table_stats)
			end
			table_sort(table_list,table_state_sort)
		end
	else
		tablesvr_stats_list = {}
		tablesvr_stats_list[table_stats.table_type] = {table_stats}
		tablesvr_table_player_num[tablesvr_id] = tablesvr_stats_list
	end
--    print_r(tablesvr_table_player_num)
end

local function add_users_try_match_times(users_data)
	for _, user_data in pairs(users_data) do
		user_data.try_match_times = user_data.try_match_times + 1
	end
end

local function register_player_on_table(tablesvr_id,table_data,users_data,table_pattern,table_conf)
	--print("register_player_on_table ",tostring_r(table_data),tostring_r(users_data),table_pattern,tostring_r(table_conf))

    local conf = table_conf or {}
    conf.table_pattern = table_pattern

	local dest = R().tablesvr(tablesvr_id):dest()
	--有可能网络不通 也有可能远程房间已经不存在导致产生的错误 此时需要将玩家进行送回到匹配队列中去
	local ok,result = R().dest(dest):call('.table_mgr','newmatch_register_all',
                                          table_data,users_data,conf)
	if not ok then
		--增加每个玩家的匹配次数
		add_users_try_match_times(users_data)
		return_back_player(table_data.table_type,users_data)
		errlog(uid,'failed to register player on table ',ok,result,tostring_r(users_data),tostring_r(table_data))
    	return false
	end

	if not result then
		--匹配失败，不响应给玩家
		--增加每个玩家的匹配次数
		add_users_try_match_times(users_data)
		return_back_player(table_data.table_type,users_data)
		errlog(uid,'failed to register players on table',tostring_r(users_data))
		return false
	end

	--如果matched_gid不等于0那么就是分配新的桌子
	if table_data.matched_gid ~= 0 then
        --停服或者网络延迟tablesvr_table_num已被清理
		tablesvr_table_num[tablesvr_id] = (tablesvr_table_num[tablesvr_id] or 0) + 1
	end

	--失败的话需要将失败列表进行回归到匹配列表中去
	local table_stats = result.table_stats
	if #result.failed_list > 0 then
		add_users_try_match_times(result.failed_list)
		return_back_player(table_data.table_type,result.failed_list)
	end

	--通知成功的玩家列表
	if result.success_list then
		for _,uid in pairs(result.success_list) do
			--print("===============player_match_type_map[uid]=nil==============",uid)
			player_match_type_map[uid] = nil
			send_data_to_player(uid,"report_player_matched",dest,table_stats.table_gid,table_stats.game_type)
		end
	end

	if not result.no_update then
		update_table_stats(result.table_stats)
	end

	return true
end

local function safe_register(...)
	--注册玩家
	local ok,ret = xpcall(register_player_on_table,debug.traceback,...)
	if not ok then
		errlog(uid,'failed to register_player_on_table',ret)
	end
end

local function handle_matched_player()
	-- if DEBUG and next(matched_list) then
	-- 	print_r(matched_list)
	-- end
    local now = util.get_now_time()
    for index, match_data in pairs(hunderd_list) do
        if now >= match_data.delay_time then
            local table_data = {
             matched_gid = match_data.matched_gid,
             table_gid = match_data.table_gid,
             table_type = match_data.table_type,
            }
            skynet.fork(safe_register,match_data.tablesvr_id,table_data,match_data.users_data,
                    match_data.table_pattern,match_data.table_conf)
            hunderd_list[index] = nil
            break
        end
    end

	local match_data = table_remove(matched_list,1)
	while match_data do
		local table_data = {
			matched_gid = match_data.matched_gid,
			table_gid = match_data.table_gid,
			table_type = match_data.table_type,
	    }

	    skynet.fork(safe_register,match_data.tablesvr_id,table_data,match_data.users_data,
                    match_data.table_pattern,match_data.table_conf)
		match_data = table_remove(matched_list,1)
	end
end

function do_match_runable()
    for table_type,match_type_list in pairs(match_data_list) do
        local ok,ret = xpcall(match_player,debug.traceback,table_type,match_type_list)
        if not ok then
            errlog(uid,'failed to match_runable',ret)
        end
    end

    handle_matched_player()
end

local function match_runable()
	while true do
        local ok, ret = xpcall(do_match_runable, debug.traceback)
        if not ok then
            errlog(ret)
        end

        skynet.sleep(20) --20ms一次
    end
end

local function check_hunderd_list(type)
    for _,v in pairs(hunderd_list) do
        if v.table_type == type then
            return false
        end
    end
    return true  
end

local function do_load_br()
  --  register_player_on_table
    --dbglog("do_load_br ")
    if not start_load then return end 
    start_load = false
    local roomdata = global_configs.roomdata
    local now = util.get_now_time()
    for type,data in pairs(roomdata) do
           dbglog("do_load_br:",type,data.is_hundred_game,data.open_game)
        if data.is_hundred_game == true and get_table_type_num(type) == 0 and data.open_game == true and check_hunderd_list(type) and 
            table_def.GAME_TYPE_HONGBAO ~= game_def.get_game_type(type) then
            local delay_time = now + math_random(1, 10) --1到10秒随机创建
            local tablesvr_id = select_one_table_server(type) 
            if not tablesvr_id then
                goto continue
            end
            dbglog("do_load_br :",type)
            local matched_gid = tablesvr_id << 32 | type
            table_insert(hunderd_list, {
            tablesvr_id = tablesvr_id, 
            matched_gid = matched_gid, 
            table_gid   = 0,
            table_type  = type, 
            users_data  = {},
            table_pattern = table_patterns.LEGACY, 
            table_conf  = nil,
            delay_time = delay_time,})

            ::continue::       
        end
    end
end

--初始化百人场
local function load_br()
    skynet.sleep(600) 
    while true do
        local ok, ret = xpcall(do_load_br, debug.traceback)
        if not ok then
            errlog(ret)
        end
        skynet.sleep(6000) --60ms一次
    end   
end

function do_check_match_timeout()
    local now = util.get_now_time()
    local remove_uids = {}
    for uid, match_time in pairs(player_match_type_map) do
        if now - match_time >= clear_match_status_time_out then
            table_insert(remove_uids,uid)
        end
    end

    for _, uid in pairs(remove_uids) do
        player_match_type_map[uid] = nil
    end
end

local function check_match_timeout()
	while true do
        local ok, ret = xpcall(do_check_match_timeout, debug.traceback)
        if not ok then
            errlog(ret)
        end

		skynet.sleep(1000)
	end
end

--清除桌子状态
local function clear_table_stats(server_id)
	if tablesvr_table_player_num[server_id] then
		dbglog(server_id,"=============clear_table_stats==============")
		tablesvr_table_player_num[server_id] = nil
		--此时先暴力的标记一下该桌子关闭掉了
		closing_tablesvr_map[server_id] = true
		tablesvr_ack_time[server_id] = nil
		tablesvr_table_num[server_id] = nil
        tablesvr_game_type_map[server_id] = nil
	end
end

function do_runable_table_info()
    local now = util.get_now_time()
    local interval = now - last_sync_time
    local phase = now - server_start_time
    
    --检查最后一次同步的时间是否大于每个table_svr回包的消息
    for server_id, ack_time in pairs(tablesvr_ack_time) do
        --如果回包的时间比同步的时间还慢 那么认为是超时了 现在只考虑桌子节点挂掉了
        --CTODO 暂时不考虑网络抖动情况
        if ack_time < last_sync_time then
            errlog(server_id, "syn_runable_table_info is timeout", ack_time, last_sync_time)
            clear_table_stats(server_id)
        end
    end

    if (phase <= phase_swith_time and interval >= 5) or
        phase > phase_swith_time and interval >= 20 then
        last_sync_time = now
        --同步所有桌子节点的桌子信息
        R().tablesvr():broadcast('.table_mgr','sync_table_stats',R().get_source(),'.table_mgr',server_start_time)
    end
end

--服务启动同步所有的桌子信息
local function syn_runable_table_info()
	while true do
        local ok, ret = xpcall(do_runable_table_info,debug.traceback)
        if not ok then
            errlog(ret)
        end

		skynet.sleep(500) --5 seconds
	end
end

local function _match_player(user_data)
	local uid = user_data.uid

	--玩家已经在匹配队列里面
	if player_match_type_map[uid] then
		dbglog(uid,"player is already in match  list!!!")
		return false
	end

	if not match_data_list[user_data.table_type] then
		match_data_list[user_data.table_type] = {}
	end

    user_data.try_match_times = 0
    table_type_ip_limit[user_data.table_type] = user_data.ip_limit

	table_insert(match_data_list[user_data.table_type],user_data)
	table_sort(match_data_list[user_data.table_type],match_sort)

	player_match_type_map[uid] = util.get_now_time()

	return true
end

function get_trend_info(tablesvr_id,table_gid)
    print("======stats.table_gid=======",tablesvr_id,table_gid)
    local ok,data = R().tablesvr(tablesvr_id):call('.table_mgr','get_trend_info',table_gid)
    print_r(data)
    if ok then
        print("get_trend_info table_gid",table_gid)
        return data
    else
        return {}
    end
end

function CMD.stop_table_server(server_id)
    --直接清理桌子？？[add by fg 2018/09/30]
    dbglog("recv stop_table_server",server_id)
    clear_table_stats(server_id)
end

function CMD.match_player(user_data)
	--print("===================match_player============",tostring_r(user_data))
	return skynet.retpack(_match_player(user_data))
end

--取桌子gid
function CMD.get_table_gid(game_type,table_type)
    dbglog("get_table_gid",game_type,table_type)
    print_r(tablesvr_table_player_num)
    for tablesvr_id,table_type_stats_list in pairs(tablesvr_table_player_num) do
        if not is_table_svr_close(tablesvr_id) then
            for key,table_stats_list in pairs(table_type_stats_list) do
                if key == table_type then
                    for _,stats in pairs(table_stats_list) do
                        return skynet.retpack(stats.table_gid)
                    end
                end
            end
        end
    end
    return skynet.retpack(false)
end

--取桌子gid列表
function CMD.get_gid_list(game_type)
    -- print("======stats.table_gid===", game_type)
    local gid_list = {}
    for tablesvr_id,table_type_stats_list in pairs(tablesvr_table_player_num) do
        if not is_table_svr_close(tablesvr_id) then
            for table_type,table_stats_list in pairs(table_type_stats_list) do
                if game_def.get_game_type(table_type) == game_type then
                    for _,stats in pairs(table_stats_list) do
                        print("======stats.table_gid=======",stats.table_gid,game_type)
                        table_insert(gid_list,stats.table_gid)
                    end
                    
                end
            end
        end
    end  
    return skynet.retpack(gid_list)
end

function CMD.report_table_stats(server_id,game_type,closing_server,curr_table_num,table_type_stats_list)
	--先对每种类型的桌子按照剩余人数进行排序
	if table_type_stats_list then
		for table_type, table_list in pairs(table_type_stats_list) do
			table_sort(table_list, table_state_sort)
		end
		tablesvr_table_player_num[server_id] = table_type_stats_list
	end
  --  print_r(table_type_stats_list)

	closing_tablesvr_map[server_id] = closing_server
	tablesvr_ack_time[server_id] = util.get_now_time()
	tablesvr_table_num[server_id] = curr_table_num
    tablesvr_game_type_map[server_id] = game_type
    start_load = true

end

function CMD.update_table_stats(table_stats)
   -- dbglog("xxxxxxxxxxxxxxxxxxxxx11111111111 CMD.update_table_stats")
	update_table_stats(table_stats)
end

function CMD.delete_table_stats(table_stats)
	delete_table_stats(table_stats)
end

--解锁指定的桌子(方便线上应急处理)
function CMD.delete_table_by_id(table_type)
    if not table_type then 
        return 
    end
    print("__1__tablesvr_table_player_num__", tablesvr_table_player_num)
    for tablesvr_id, table_type_stats_list in pairs(tablesvr_table_player_num) do
        table_type_stats_list[table_type] = nil
    end
    print("__2__tablesvr_table_player_num__", tablesvr_table_player_num)
end




function CMD.start()
	server_start_time = util.get_now_time()

	skynet.fork(syn_runable_table_info)
	skynet.fork(match_runable)
	skynet.fork(check_match_timeout)
    skynet.fork(load_br)
	skynet.retpack(true)
end

function dispatch_cmd(session,source,action,...)
    CMD[action](...)
end

function start()
    skynet.start(function()
        skynet.dispatch("lua", function(session,source,action,...)
            dispatch_cmd(session,source,action,...)
        end)

        -- sharedata.query("global_configs")
        -- global_configs = setmetatable({},{
        --     __index = function(t,k)
        --         return sharedata.query("global_configs")[k]
        --     end
        -- })
        global_configs = setmetatable({},{
            __index = function(t,k) 
                --return sharedata.query("global_configs")[k]
                return sharedata.query(k)
            end
        })
    end)
end

_START(function()
    print('now this service is starting')
    init()
    start()
end)


